Geo data and April data collections
- Note: This chunk needn’t run again.
# Select all tweets in April 2020
if(F){
paste("CREATE TABLE CoronavirusTweets",
" AS SELECT * FROM CoronavirusTweetsCsv",
" WHERE (strftime('%Y-%m-%d %H:%M:%S',created_at)>=",
"strftime('%Y-%m-%d %H:%M:%S','2020-03-29 00:00:00'))",
" AND (strftime('%Y-%m-%d %H:%M:%S',created_at)<=",
"strftime('%Y-%m-%d %H:%M:%S','2020-04-29 23:59:59'))",sep='')%>%
dbSendQuery(conn,.)
April_tweet=paste("SELECT Tweet_ID FROM CoronavirusTweets",sep='')%>%
dbGetQuery(conn,.)
# Set Twitter developer account
create_token(app='MSSP-An-Auxiliary-Tool',
consumer_key='ORvbA3CEOP06hi9MHfz7yknwV',
consumer_secret='nAy2PRkiV4AYZ0NvHAF6Iw0IBFrttWMKTuxXbUWN4bcZnMpTQR',
access_token='1328377313562509313-j1iSFuJLLo3FL768jdnHKe1fzmcWnS',
access_secret='oJIhGoThBNSBSMLQBo3AxS5kcLrqq8sCjx6OIPX9NRmPT')
for(i in 1:ceiling(nrow(April_tweet)/90000)) {
rl=rate_limit("lookup_statuses")
if(rl%>%select(remaining)!=900){
rl%>%select(reset)*60%>%ceiling()%>%Sys.sleep()
}
april_tweet=lookup_statuses(April_tweet$Tweet_ID[(900*i):nrow(April_tweet)])
if(i==1){April_tweet=april_tweet}else{April_tweet=rbind(April_tweet,april_tweet)}
}
April_tweet%>%
select(status_id,user_id,screen_name,created_at,text,is_quote,
is_retweet,favourites_count,retweet_count,followers_count,
friends_count,lang)%>%
dbWriteTable(conn,'CoronavirusTweets',.)
}
# Select all tweets with geo information from 202001 to 202011
if(F){
paste("CREATE TABLE CoronavirusTweetsGeo",
" AS SELECT * FROM CoronavirusTweetsCsv",
" WHERE Geolocation_coordinate='YES'",sep='')%>%
dbSendQuery(conn,.)
Geo_tweet=paste("SELECT Tweet_ID FROM CoronavirusTweetsGeo",sep='')%>%
dbGetQuery(conn,.)
for(i in 1:ceiling(nrow(Geo_tweet)/90000)) {
rl=rate_limit("lookup_statuses")
if(rl%>%select(remaining)!=900){
rl%>%select(reset)*60%>%ceiling()%>%Sys.sleep()
}
geo=lookup_statuses(Geo_tweet$Tweet_ID[(900*i):nrow(Geo_tweet)])
if(i==1){Geo=geo}else{Geo=rbind(Geo,geo)}
}
lat_lng(Geo)%>%
select(status_id,user_id,screen_name,created_at,text,is_quote,
is_retweet,favourites_count,retweet_count,followers_count,
friends_count,lang,place_full_name,place_type,country_code,
place_name,country,lat,lng)%>%
dbWriteTable(conn,'CoronavirusTweetsGeo',.)
# Delete initial collection of covid tweets csv files table
"DROP TABLE CoronavirusTweetsCsv" %>%
dbSendQuery(conn,.)
# Create index to accelerate query
paste("CREATE INDEX CT_status_id ON CoronavirusTweets(status_id);",
"CREATE INDEX CTG_status_id ON CoronavirusTweetsGeo(status_id);",
"CREATE INDEX TS_status_id ON TweetsSentiment(status_id);",
"CREATE INDEX TGS_status_id ON TweetsGeoSentiment(status_id);",
"CREATE INDEX CTG_lat_long ON CoronavirusTweetsGeo(lat,lng);",
"CREATE UNIQUE INDEX GD_lat_long ON GeoDetail(lat,lng)")%>%
dbSendQuery(conn,.)
}
dbDisconnect(conn)
Get data function
getTwitterData=function(conn,geoinfo=T,keywords=NULL,
period=c('2020-03-29 00:00:00','2020-04-30 23:59:59')){
# Select table of database according to 'geoinfo'
if(geoinfo){
geoinfo_query=paste("SELECT CoronavirusTweetsGeo.*,",
"city,state,country,sentiment_score ",
"FROM CoronavirusTweetsGeo ",
"LEFT JOIN TweetsGeoSentiment ON ",
"CoronavirusTweetsGeo.status_id=",
"TweetsGeoSentiment.status_id ",
"LEFT JOIN GeoDetail ON ",
"CoronavirusTweetsGeo.lat=GeoDetail.lat ",
"AND CoronavirusTweetsGeo.lng=GeoDetail.lng",sep="")
}
else{
geoinfo_query=paste("SELECT CoronavirusTweets.*,sentiment_score ",
"FROM CoronavirusTweets ",
"LEFT JOIN TweetsSentiment ON ",
"CoronavirusTweets.status_id=",
"TweetsSentiment.status_id",sep="")
}
# Add keywords conditions according to 'keywords'
if(length(keywords==0)){
keywords_query=''
}
else{
for(i in 1:length(keywords)){
if(i==1){
keywords_query=paste(" ((text LIKE '%",keywords[i],"%')",sep="")
}
else{
keywords_query=keywords_query%>%
paste("OR (text LIKE '%",keywords[i],"%')",sep="")
}
}
keywords_query=paste(keywords_query,") ",sep="")
}
# Add period conditions according to 'period'
if(length(period)!=2){
period_query=''
}
else{
period_query=paste(" (strftime('%Y-%m-%d %H:%M:%S',created_at)>=",
"strftime('%Y-%m-%d %H:%M:%S','",period[1],"') ",
"AND strftime('%Y-%m-%d %H:%M:%S',created_at)<=",
"strftime('%Y-%m-%d %H:%M:%S','",period[2],"')) ",
sep="")
}
# Write SQL
if(period_query==''){
if(keywords_query==''){
query=paste(geoinfo_query,sep="")
}
else{
query=paste(geoinfo_query," WHERE",keywords_query,sep="")
}
}
else{
if(keywords_query==''){
query=paste(geoinfo_query," WHERE",period_query,sep="")
}
else{
query=paste(geoinfo_query," WHERE",
period_query,"AND",keywords_query,sep="")
}
}
# Obtain Data
dbGetQuery(conn,query)
}
Get trend function
getTwitterTrend=function(conn,geoinfo='country',trend='day',keywords=NULL,
period=c('2020-03-29 00:00:00','2020-04-30 23:59:59')){
# Add trend cconditions according to 'trend'
if(trend=='day'){
trend_query=c("'%Y-%m-%d'","date")
}
else{
if(trend=='week'){
trend_query=c("'%W'","week")
}
else{
if(trend=='month'){
trend_query=c("'%m'","month")
}
else{
stop("The trend can only be 'day', 'week' or 'month'.")
}
}
}
# Select table of database according to 'geoinfo'
if(is.null(geoinfo)){
geoinfo_query=paste("SELECT strftime(",trend_query[1],
",created_at) AS ",trend_query[2],", ",
"count(*) AS number, ",
"avg(sentiment_score) AS sentiment_score ",
"FROM CoronavirusTweets ",
"LEFT JOIN TweetsSentiment ON ",
"CoronavirusTweets.status_id=",
"TweetsSentiment.status_id",sep="")
group_query=paste(" GROUP BY strftime(",trend_query[1],
",created_at)",sep="")
}
else{
if(geoinfo=='country'){
geoinfo_query=paste("SELECT strftime(",trend_query[1],
",created_at) AS ",trend_query[2],", ",
"count(*) AS number, country, ",
"avg(sentiment_score) AS sentiment_score ",
"FROM CoronavirusTweetsGeo ",
"LEFT JOIN TweetsGeoSentiment ON ",
"CoronavirusTweetsGeo.status_id=",
"TweetsGeoSentiment.status_id ",
"LEFT JOIN GeoDetail ON ",
"CoronavirusTweetsGeo.lat=GeoDetail.lat ",
"AND CoronavirusTweetsGeo.lng=GeoDetail.lng",sep="")
group_query=paste(" GROUP BY strftime(",trend_query[1],
",created_at),country",sep="")
}
else{
if(geoinfo=='state'){
geoinfo_query=paste("SELECT strftime(",trend_query[1],
",created_at) AS ",trend_query[2],", ",
"count(*) AS number, country, state, ",
"avg(sentiment_score) AS sentiment_score ",
"FROM CoronavirusTweetsGeo ",
"LEFT JOIN TweetsGeoSentiment ON ",
"CoronavirusTweetsGeo.status_id=",
"TweetsGeoSentiment.status_id ",
"LEFT JOIN GeoDetail ON ",
"CoronavirusTweetsGeo.lat=GeoDetail.lat ",
"AND CoronavirusTweetsGeo.lng=GeoDetail.lng",sep="")
group_query=paste(" GROUP BY strftime(",trend_query[1],
",created_at),country,state",sep="")
}
else{
if(geoinfo=='city'){
geoinfo_query=paste("SELECT strftime(",trend_query[1],
",created_at) AS ",trend_query[2],", ",
"count(*) AS number, country, state, city, ",
"avg(sentiment_score) AS sentiment_score ",
"FROM CoronavirusTweetsGeo ",
"LEFT JOIN TweetsGeoSentiment ON ",
"CoronavirusTweetsGeo.status_id=",
"TweetsGeoSentiment.status_id ",
"LEFT JOIN GeoDetail ON ",
"CoronavirusTweetsGeo.lat=GeoDetail.lat ",
"AND CoronavirusTweetsGeo.lng=GeoDetail.lng",
sep="")
group_query=paste(" GROUP BY strftime(",trend_query[1],
",created_at),country,state,city",sep="")
}
else{
stop("The geoinfo can only be 'NULL', 'city', 'state' or 'country'.")
}
}
}
}
# Add keywords conditions according to 'keywords'
if(is.null(keywords)){
keywords_query=''
}
else{
for(i in 1:length(keywords)){
if(i==1){
keywords_query=paste(" ((text LIKE '%",keywords[i],"%')",sep="")
}
else{
keywords_query=keywords_query%>%
paste("OR (text LIKE '%",keywords[i],"%')",sep="")
}
}
keywords_query=paste(keywords_query,") ",sep="")
}
# Add period conditions according to 'period'
if(is.null(period)){
period_query=''
}
else{
if(length(period)==2){
period_query=paste(" (strftime('%Y-%m-%d %H:%M:%S',created_at)>=",
"strftime('%Y-%m-%d %H:%M:%S','",period[1],"') ",
"AND strftime('%Y-%m-%d %H:%M:%S',created_at)<=",
"strftime('%Y-%m-%d %H:%M:%S','",period[2],"')) ",
sep="")
}
else{
stop("The time period should be a vector with length 2.")
}
}
# Write SQL
if(period_query==''){
if(keywords_query==''){
query=paste(geoinfo_query,group_query,sep="")
}
else{
query=paste(geoinfo_query," WHERE",keywords_query,group_query,sep="")
}
}
else{
if(keywords_query==''){
query=paste(geoinfo_query," WHERE",period_query,group_query,sep="")
}
else{
query=paste(geoinfo_query," WHERE",period_query,"AND",keywords_query,
group_query,sep="")
}
}
# Obtain Data
dbGetQuery(conn,query)
}
# Add keywords conditions according to 'keywords'
if(is.null(keywords)){
keywords_query=''
}
else{
for(i in 1:length(keywords)){
if(i==1){
keywords_query=paste(" ((text LIKE '%",keywords[i],"%')",sep="")
}
else{
keywords_query=keywords_query%>%
paste("OR (text LIKE '%",keywords[i],"%')",sep="")
}
}
keywords_query=paste(keywords_query,") ",sep="")
}
# Add period conditions according to 'period'
if(is.null(period)){
period_query=''
}
else{
if(length(period)==2){
period_query=paste(" (strftime('%Y-%m-%d %H:%M:%S',created_at)>=",
"strftime('%Y-%m-%d %H:%M:%S','",period[1],"') ",
"AND strftime('%Y-%m-%d %H:%M:%S',created_at)<=",
"strftime('%Y-%m-%d %H:%M:%S','",period[2],"')) ",
sep="")
}
else{
stop("The time period should be a vector with length 2.")
}
}
# Write SQL
if(period_query==''){
if(keywords_query==''){
query=paste(geoinfo_query,group_query,sep="")
}
else{
query=paste(geoinfo_query," WHERE",keywords_query,group_query,sep="")
}
}
else{
if(keywords_query==''){
query=paste(geoinfo_query," WHERE",period_query,group_query,sep="")
}
else{
query=paste(geoinfo_query," WHERE",period_query,"AND",keywords_query,
group_query,sep="")
}
}
# Obtain Data
dbGetQuery(conn,query)
}
# connect to database
dbpath="/Users/mac/Desktop/Covid-tweets-en.db"
conn=dbConnect(SQLite(),dbpath)
# load spread data
spread_data=read.csv("/Users/mac/Desktop/shiny/us_covid19_daily.csv",header=TRUE)%>%
transmute(date=ymd(date),
positiveIncrease=positiveIncrease,
deathIncrease=deathIncrease)%>%
arrange(desc(date))%>%
.[68:100,]
Geoplot=getTwitterTrend(conn,geoinfo='state',period=NULL)%>%
filter(country=='United States')%>%
{merge(read.csv("/Users/mac/Desktop/shiny/us_states_daily.csv",header=TRUE),.,by="state")}%>%
{mutate(.,hover=with(.,paste(state,
"<br> <br> Positive:",positive,
"<br> death:",death,
"<br> number of tweets",number,
"<br> sentiment score of this state",sentiment_score)))}
ay=list(tickfont=list(color="red"),overlaying="y",side="right",title="frequency")
g <- list(
scope = 'usa',
projection = list(type = 'albers usa'),
showlakes = TRUE,
lakecolor = toRGB('white'))
Visualization
#data1
fig=plot_ly(data_1,x=~date,y=~positiveIncrease,color=I('black'),
name='positiveIncrease',type ='scatter',mode='lines+markers')
n=nrow(data_1)
color=data$sentiment%>%
{(.[1:(n-1)]+.[2:n])/2}%>%
{ifelse(.>0,'green','red')}
fig=fig%>%
add_trace(x=data_1$date[1:2],y=data_1$frequency[1:2],color=I('blue'),
name=input[1],mode='lines+markers',yaxis="y2",
visible='legendonly')
for(i in 1:(n-1))
fig=fig%>%
add_trace(x=data_1$date[i:(i+1)],y=data_1$frequency[i:(i+1)],color=I(color[i]),
marker=list(symbol = 2,size=10),showlegend=F,mode='lines+markers',yaxis="y2")
fig%>%layout(title="Twitter Sentiment trends",yaxis2=ay,xaxis=list(title="x"))
LS0tCnRpdGxlOiAiQ292aWQtVHdlZXRzIgphdXRob3I6ICJHcm91cDI6RGFucGluZyBMaXUsIEhhbyBTaGVuLCBIYW9xaSBXYW5nLCBZdXhpIFdhbmciCmRhdGU6ICIyMDIwLzEyLzIiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIEluaXRpYWwgc2V0dGluZwoqIFRoZSBkYXRhYmFzZSBjYW4gYmUgZG93bmxvYW5kIGZyb20gW09uZURyaXZlXShodHRwczovLzFkcnYubXMvdS9zIUF0b0EtUk15THBmMmhPMXJPODJwUDJ5OE9NZmctZz9lPWF6Zko0NCkKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKcGFjbWFuOjpwX2xvYWQobGlicmFyeXNoaW55LHRpZHl2ZXJzZSxsdWJyaWRhdGUsREJJLFJTUUxpdGUscGxvdGx5KQpkYnBhdGg9Ii9Vc2Vycy9tYWMvRGVza3RvcC9Db3ZpZC10d2VldHMtZW4uZGIiCmNvbm49ZGJDb25uZWN0KFNRTGl0ZSgpLGRicGF0aCkKYGBgCgojIyBEYXRhYmFzZSBwYXJ0CiMjIEdlbyBkYXRhIGFuZCBBcHJpbCBkYXRhIGNvbGxlY3Rpb25zCiogTm90ZTogVGhpcyBjaHVuayBuZWVkbid0IHJ1biBhZ2Fpbi4KYGBge3J9CiMgU2VsZWN0IGFsbCB0d2VldHMgaW4gQXByaWwgMjAyMAppZihGKXsKcGFzdGUoIkNSRUFURSBUQUJMRSBDb3JvbmF2aXJ1c1R3ZWV0cyIsCiAgICAgICIgQVMgU0VMRUNUICogRlJPTSBDb3JvbmF2aXJ1c1R3ZWV0c0NzdiIsCiAgICAgICIgV0hFUkUgKHN0cmZ0aW1lKCclWS0lbS0lZCAlSDolTTolUycsY3JlYXRlZF9hdCk+PSIsCiAgICAgICJzdHJmdGltZSgnJVktJW0tJWQgJUg6JU06JVMnLCcyMDIwLTAzLTI5IDAwOjAwOjAwJykpIiwKICAgICAgIiBBTkQgKHN0cmZ0aW1lKCclWS0lbS0lZCAlSDolTTolUycsY3JlYXRlZF9hdCk8PSIsCiAgICAgICJzdHJmdGltZSgnJVktJW0tJWQgJUg6JU06JVMnLCcyMDIwLTA0LTI5IDIzOjU5OjU5JykpIixzZXA9JycpJT4lCiAgZGJTZW5kUXVlcnkoY29ubiwuKQogIApBcHJpbF90d2VldD1wYXN0ZSgiU0VMRUNUIFR3ZWV0X0lEIEZST00gQ29yb25hdmlydXNUd2VldHMiLHNlcD0nJyklPiUKICBkYkdldFF1ZXJ5KGNvbm4sLikKIyBTZXQgVHdpdHRlciBkZXZlbG9wZXIgYWNjb3VudApjcmVhdGVfdG9rZW4oYXBwPSdNU1NQLUFuLUF1eGlsaWFyeS1Ub29sJywKICAgICAgICAgICAgIGNvbnN1bWVyX2tleT0nT1J2YkEzQ0VPUDA2aGk5TUhmejd5a253VicsCiAgICAgICAgICAgICBjb25zdW1lcl9zZWNyZXQ9J25BeTJQUmtpVjRBWVowTnZIQUY2SXcwSUJGcnR0V01LVHV4WGJVV040YmNabk1wVFFSJywKICAgICAgICAgICAgIGFjY2Vzc190b2tlbj0nMTMyODM3NzMxMzU2MjUwOTMxMy1qMWlTRnVKTExvM0ZMNzY4amRuSEtlMWZ6bWNXblMnLAogICAgICAgICAgICAgYWNjZXNzX3NlY3JldD0nb0pJaEdvVGhCTlNCU01MUUJvM0F4UzVrY0xycXE4c0NqeDZPSVBYOU5SbVBUJykKZm9yKGkgaW4gMTpjZWlsaW5nKG5yb3coQXByaWxfdHdlZXQpLzkwMDAwKSkgewogIHJsPXJhdGVfbGltaXQoImxvb2t1cF9zdGF0dXNlcyIpCiAgaWYocmwlPiVzZWxlY3QocmVtYWluaW5nKSE9OTAwKXsKICAgIHJsJT4lc2VsZWN0KHJlc2V0KSo2MCU+JWNlaWxpbmcoKSU+JVN5cy5zbGVlcCgpCiAgfQogIGFwcmlsX3R3ZWV0PWxvb2t1cF9zdGF0dXNlcyhBcHJpbF90d2VldCRUd2VldF9JRFsoOTAwKmkpOm5yb3coQXByaWxfdHdlZXQpXSkKICBpZihpPT0xKXtBcHJpbF90d2VldD1hcHJpbF90d2VldH1lbHNle0FwcmlsX3R3ZWV0PXJiaW5kKEFwcmlsX3R3ZWV0LGFwcmlsX3R3ZWV0KX0KfQogIEFwcmlsX3R3ZWV0JT4lCiAgc2VsZWN0KHN0YXR1c19pZCx1c2VyX2lkLHNjcmVlbl9uYW1lLGNyZWF0ZWRfYXQsdGV4dCxpc19xdW90ZSwKICAgICAgICAgaXNfcmV0d2VldCxmYXZvdXJpdGVzX2NvdW50LHJldHdlZXRfY291bnQsZm9sbG93ZXJzX2NvdW50LAogICAgICAgICBmcmllbmRzX2NvdW50LGxhbmcpJT4lCiAgZGJXcml0ZVRhYmxlKGNvbm4sJ0Nvcm9uYXZpcnVzVHdlZXRzJywuKQp9CiMgU2VsZWN0IGFsbCB0d2VldHMgd2l0aCBnZW8gaW5mb3JtYXRpb24gZnJvbSAyMDIwMDEgdG8gMjAyMDExCmlmKEYpewpwYXN0ZSgiQ1JFQVRFIFRBQkxFIENvcm9uYXZpcnVzVHdlZXRzR2VvIiwKICAgICAgIiBBUyBTRUxFQ1QgKiBGUk9NIENvcm9uYXZpcnVzVHdlZXRzQ3N2IiwKICAgICAgIiBXSEVSRSBHZW9sb2NhdGlvbl9jb29yZGluYXRlPSdZRVMnIixzZXA9JycpJT4lCiAgZGJTZW5kUXVlcnkoY29ubiwuKQoKR2VvX3R3ZWV0PXBhc3RlKCJTRUxFQ1QgVHdlZXRfSUQgRlJPTSBDb3JvbmF2aXJ1c1R3ZWV0c0dlbyIsc2VwPScnKSU+JQogIGRiR2V0UXVlcnkoY29ubiwuKQoKCmZvcihpIGluIDE6Y2VpbGluZyhucm93KEdlb190d2VldCkvOTAwMDApKSB7CiAgcmw9cmF0ZV9saW1pdCgibG9va3VwX3N0YXR1c2VzIikKICBpZihybCU+JXNlbGVjdChyZW1haW5pbmcpIT05MDApewogICAgcmwlPiVzZWxlY3QocmVzZXQpKjYwJT4lY2VpbGluZygpJT4lU3lzLnNsZWVwKCkKICB9CiAgZ2VvPWxvb2t1cF9zdGF0dXNlcyhHZW9fdHdlZXQkVHdlZXRfSURbKDkwMCppKTpucm93KEdlb190d2VldCldKQogIGlmKGk9PTEpe0dlbz1nZW99ZWxzZXtHZW89cmJpbmQoR2VvLGdlbyl9Cn0KCmxhdF9sbmcoR2VvKSU+JQogIHNlbGVjdChzdGF0dXNfaWQsdXNlcl9pZCxzY3JlZW5fbmFtZSxjcmVhdGVkX2F0LHRleHQsaXNfcXVvdGUsCiAgICAgICAgIGlzX3JldHdlZXQsZmF2b3VyaXRlc19jb3VudCxyZXR3ZWV0X2NvdW50LGZvbGxvd2Vyc19jb3VudCwKICAgICAgICAgZnJpZW5kc19jb3VudCxsYW5nLHBsYWNlX2Z1bGxfbmFtZSxwbGFjZV90eXBlLGNvdW50cnlfY29kZSwKICAgICAgICAgcGxhY2VfbmFtZSxjb3VudHJ5LGxhdCxsbmcpJT4lCiAgZGJXcml0ZVRhYmxlKGNvbm4sJ0Nvcm9uYXZpcnVzVHdlZXRzR2VvJywuKQoKIyBEZWxldGUgaW5pdGlhbCBjb2xsZWN0aW9uIG9mIGNvdmlkIHR3ZWV0cyBjc3YgZmlsZXMgdGFibGUKIkRST1AgVEFCTEUgQ29yb25hdmlydXNUd2VldHNDc3YiICU+JQogIGRiU2VuZFF1ZXJ5KGNvbm4sLikKIyBDcmVhdGUgaW5kZXggdG8gYWNjZWxlcmF0ZSBxdWVyeQpwYXN0ZSgiQ1JFQVRFIElOREVYIENUX3N0YXR1c19pZCBPTiBDb3JvbmF2aXJ1c1R3ZWV0cyhzdGF0dXNfaWQpOyIsCiAgIkNSRUFURSBJTkRFWCBDVEdfc3RhdHVzX2lkIE9OIENvcm9uYXZpcnVzVHdlZXRzR2VvKHN0YXR1c19pZCk7IiwKICAiQ1JFQVRFIElOREVYIFRTX3N0YXR1c19pZCBPTiBUd2VldHNTZW50aW1lbnQoc3RhdHVzX2lkKTsiLAogICJDUkVBVEUgSU5ERVggVEdTX3N0YXR1c19pZCBPTiBUd2VldHNHZW9TZW50aW1lbnQoc3RhdHVzX2lkKTsiLAogICJDUkVBVEUgSU5ERVggQ1RHX2xhdF9sb25nIE9OIENvcm9uYXZpcnVzVHdlZXRzR2VvKGxhdCxsbmcpOyIsCiAgIkNSRUFURSBVTklRVUUgSU5ERVggR0RfbGF0X2xvbmcgT04gR2VvRGV0YWlsKGxhdCxsbmcpIiklPiUKICBkYlNlbmRRdWVyeShjb25uLC4pCn0KZGJEaXNjb25uZWN0KGNvbm4pCmBgYAoKIyMgR2V0IGRhdGEgZnVuY3Rpb24KYGBge3J9CiMgZ2V0IFR3aXR0ZXIgRGF0YSBmdW5jdGlvbgpnZXRUd2l0dGVyRGF0YT1mdW5jdGlvbihjb25uLGdlb2luZm89VCxrZXl3b3Jkcz1OVUxMLAogICAgICAgICAgICAgICAgICAgICAgICBwZXJpb2Q9YygnMjAyMC0wMy0yOSAwMDowMDowMCcsJzIwMjAtMDQtMzAgMjM6NTk6NTknKSl7CiAgIyBTZWxlY3QgdGFibGUgb2YgZGF0YWJhc2UgYWNjb3JkaW5nIHRvICdnZW9pbmZvJwogIGlmKGdlb2luZm8pewogICAgZ2VvaW5mb19xdWVyeT1wYXN0ZSgiU0VMRUNUIENvcm9uYXZpcnVzVHdlZXRzR2VvLiosIiwKICAgICAgICAgICAgICAgICAgICAgICAgImNpdHksc3RhdGUsY291bnRyeSxzZW50aW1lbnRfc2NvcmUgIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkZST00gQ29yb25hdmlydXNUd2VldHNHZW8gIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkxFRlQgSk9JTiBUd2VldHNHZW9TZW50aW1lbnQgT04gIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkNvcm9uYXZpcnVzVHdlZXRzR2VvLnN0YXR1c19pZD0iLAogICAgICAgICAgICAgICAgICAgICAgICAiVHdlZXRzR2VvU2VudGltZW50LnN0YXR1c19pZCAiLAogICAgICAgICAgICAgICAgICAgICAgICAiTEVGVCBKT0lOIEdlb0RldGFpbCBPTiAiLAogICAgICAgICAgICAgICAgICAgICAgICAiQ29yb25hdmlydXNUd2VldHNHZW8ubGF0PUdlb0RldGFpbC5sYXQgIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkFORCBDb3JvbmF2aXJ1c1R3ZWV0c0dlby5sbmc9R2VvRGV0YWlsLmxuZyIsc2VwPSIiKQogIH0KICBlbHNlewogICAgZ2VvaW5mb19xdWVyeT1wYXN0ZSgiU0VMRUNUIENvcm9uYXZpcnVzVHdlZXRzLiosc2VudGltZW50X3Njb3JlICIsCiAgICAgICAgICAgICAgICAgICAgICAgICJGUk9NIENvcm9uYXZpcnVzVHdlZXRzICIsCiAgICAgICAgICAgICAgICAgICAgICAgICJMRUZUIEpPSU4gVHdlZXRzU2VudGltZW50IE9OICIsCiAgICAgICAgICAgICAgICAgICAgICAgICJDb3JvbmF2aXJ1c1R3ZWV0cy5zdGF0dXNfaWQ9IiwKICAgICAgICAgICAgICAgICAgICAgICAgIlR3ZWV0c1NlbnRpbWVudC5zdGF0dXNfaWQiLHNlcD0iIikKICB9CiAgIyBBZGQga2V5d29yZHMgY29uZGl0aW9ucyBhY2NvcmRpbmcgdG8gJ2tleXdvcmRzJyAKICBpZihsZW5ndGgoa2V5d29yZHM9PTApKXsKICAgIGtleXdvcmRzX3F1ZXJ5PScnCiAgfQogIGVsc2V7CiAgICBmb3IoaSBpbiAxOmxlbmd0aChrZXl3b3JkcykpewogICAgICBpZihpPT0xKXsKICAgICAgICBrZXl3b3Jkc19xdWVyeT1wYXN0ZSgiICgodGV4dCBMSUtFICclIixrZXl3b3Jkc1tpXSwiJScpIixzZXA9IiIpCiAgICAgIH0KICAgICAgZWxzZXsKICAgICAgICBrZXl3b3Jkc19xdWVyeT1rZXl3b3Jkc19xdWVyeSU+JQogICAgICAgICAgcGFzdGUoIk9SICh0ZXh0IExJS0UgJyUiLGtleXdvcmRzW2ldLCIlJykiLHNlcD0iIikKICAgICAgfQogICAgfQogICAga2V5d29yZHNfcXVlcnk9cGFzdGUoa2V5d29yZHNfcXVlcnksIikgIixzZXA9IiIpCiAgfQogICMgQWRkIHBlcmlvZCBjb25kaXRpb25zIGFjY29yZGluZyB0byAncGVyaW9kJwogIGlmKGxlbmd0aChwZXJpb2QpIT0yKXsKICAgIHBlcmlvZF9xdWVyeT0nJwogIH0KICBlbHNlewogICAgcGVyaW9kX3F1ZXJ5PXBhc3RlKCIgKHN0cmZ0aW1lKCclWS0lbS0lZCAlSDolTTolUycsY3JlYXRlZF9hdCk+PSIsCiAgICAgICAgICAgICAgICAgICAgICAgInN0cmZ0aW1lKCclWS0lbS0lZCAlSDolTTolUycsJyIscGVyaW9kWzFdLCInKSAiLAogICAgICAgICAgICAgICAgICAgICAgICJBTkQgc3RyZnRpbWUoJyVZLSVtLSVkICVIOiVNOiVTJyxjcmVhdGVkX2F0KTw9IiwKICAgICAgICAgICAgICAgICAgICAgICAic3RyZnRpbWUoJyVZLSVtLSVkICVIOiVNOiVTJywnIixwZXJpb2RbMl0sIicpKSAiLAogICAgICAgICAgICAgICAgICAgICAgIHNlcD0iIikKICB9CiAgIyBXcml0ZSBTUUwKICBpZihwZXJpb2RfcXVlcnk9PScnKXsKICAgIGlmKGtleXdvcmRzX3F1ZXJ5PT0nJyl7CiAgICAgIHF1ZXJ5PXBhc3RlKGdlb2luZm9fcXVlcnksc2VwPSIiKQogICAgfQogICAgZWxzZXsKICAgICAgcXVlcnk9cGFzdGUoZ2VvaW5mb19xdWVyeSwiIFdIRVJFIixrZXl3b3Jkc19xdWVyeSxzZXA9IiIpCiAgICB9CiAgfQogIGVsc2V7CiAgICBpZihrZXl3b3Jkc19xdWVyeT09JycpewogICAgICBxdWVyeT1wYXN0ZShnZW9pbmZvX3F1ZXJ5LCIgV0hFUkUiLHBlcmlvZF9xdWVyeSxzZXA9IiIpCiAgICB9CiAgICBlbHNlewogICAgICBxdWVyeT1wYXN0ZShnZW9pbmZvX3F1ZXJ5LCIgV0hFUkUiLAogICAgICAgICAgICAgICAgICBwZXJpb2RfcXVlcnksIkFORCIsa2V5d29yZHNfcXVlcnksc2VwPSIiKQogICAgfQogIH0KICAjIE9idGFpbiBEYXRhCiAgZGJHZXRRdWVyeShjb25uLHF1ZXJ5KQp9CgpgYGAKCgojIyBHZXQgdHJlbmQgZnVuY3Rpb24KYGBge3J9CiMjIEdldCB0cmVuZCBmdW5jdGlvbgoKZ2V0VHdpdHRlclRyZW5kPWZ1bmN0aW9uKGNvbm4sZ2VvaW5mbz0nY291bnRyeScsdHJlbmQ9J2RheScsa2V5d29yZHM9TlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgIHBlcmlvZD1jKCcyMDIwLTAzLTI5IDAwOjAwOjAwJywnMjAyMC0wNC0zMCAyMzo1OTo1OScpKXsKICAjIEFkZCB0cmVuZCBjY29uZGl0aW9ucyBhY2NvcmRpbmcgdG8gJ3RyZW5kJwogIGlmKHRyZW5kPT0nZGF5Jyl7CiAgICB0cmVuZF9xdWVyeT1jKCInJVktJW0tJWQnIiwiZGF0ZSIpCiAgfQogIGVsc2V7CiAgICBpZih0cmVuZD09J3dlZWsnKXsKICAgICAgdHJlbmRfcXVlcnk9YygiJyVXJyIsIndlZWsiKQogICAgfQogICAgZWxzZXsKICAgICAgaWYodHJlbmQ9PSdtb250aCcpewogICAgICAgIHRyZW5kX3F1ZXJ5PWMoIiclbSciLCJtb250aCIpCiAgICAgIH0KICAgICAgZWxzZXsKICAgICAgICBzdG9wKCJUaGUgdHJlbmQgY2FuIG9ubHkgYmUgJ2RheScsICd3ZWVrJyBvciAnbW9udGgnLiIpIAogICAgICB9CiAgICB9CiAgfQogICMgU2VsZWN0IHRhYmxlIG9mIGRhdGFiYXNlIGFjY29yZGluZyB0byAnZ2VvaW5mbycKICBpZihpcy5udWxsKGdlb2luZm8pKXsKICAgIGdlb2luZm9fcXVlcnk9cGFzdGUoIlNFTEVDVCBzdHJmdGltZSgiLHRyZW5kX3F1ZXJ5WzFdLAogICAgICAgICAgICAgICAgICAgICAgICAiLGNyZWF0ZWRfYXQpIEFTICIsdHJlbmRfcXVlcnlbMl0sIiwgIiwKICAgICAgICAgICAgICAgICAgICAgICAgImNvdW50KCopIEFTIG51bWJlciwgIiwKICAgICAgICAgICAgICAgICAgICAgICAgImF2ZyhzZW50aW1lbnRfc2NvcmUpIEFTIHNlbnRpbWVudF9zY29yZSAiLAogICAgICAgICAgICAgICAgICAgICAgICAiRlJPTSBDb3JvbmF2aXJ1c1R3ZWV0cyAiLAogICAgICAgICAgICAgICAgICAgICAgICAiTEVGVCBKT0lOIFR3ZWV0c1NlbnRpbWVudCBPTiAiLAogICAgICAgICAgICAgICAgICAgICAgICAiQ29yb25hdmlydXNUd2VldHMuc3RhdHVzX2lkPSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJUd2VldHNTZW50aW1lbnQuc3RhdHVzX2lkIixzZXA9IiIpCiAgICBncm91cF9xdWVyeT1wYXN0ZSgiIEdST1VQIEJZIHN0cmZ0aW1lKCIsdHJlbmRfcXVlcnlbMV0sCiAgICAgICAgICAgICAgICAgICAgICAiLGNyZWF0ZWRfYXQpIixzZXA9IiIpCiAgfQogIGVsc2V7CiAgICBpZihnZW9pbmZvPT0nY291bnRyeScpewogICAgICBnZW9pbmZvX3F1ZXJ5PXBhc3RlKCJTRUxFQ1Qgc3RyZnRpbWUoIix0cmVuZF9xdWVyeVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAiLGNyZWF0ZWRfYXQpIEFTICIsdHJlbmRfcXVlcnlbMl0sIiwgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiY291bnQoKikgQVMgbnVtYmVyLCBjb3VudHJ5LCAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJhdmcoc2VudGltZW50X3Njb3JlKSBBUyBzZW50aW1lbnRfc2NvcmUgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiRlJPTSBDb3JvbmF2aXJ1c1R3ZWV0c0dlbyAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJMRUZUIEpPSU4gVHdlZXRzR2VvU2VudGltZW50IE9OICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvcm9uYXZpcnVzVHdlZXRzR2VvLnN0YXR1c19pZD0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICJUd2VldHNHZW9TZW50aW1lbnQuc3RhdHVzX2lkICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkxFRlQgSk9JTiBHZW9EZXRhaWwgT04gIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29yb25hdmlydXNUd2VldHNHZW8ubGF0PUdlb0RldGFpbC5sYXQgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiQU5EIENvcm9uYXZpcnVzVHdlZXRzR2VvLmxuZz1HZW9EZXRhaWwubG5nIixzZXA9IiIpCiAgICAgIGdyb3VwX3F1ZXJ5PXBhc3RlKCIgR1JPVVAgQlkgc3RyZnRpbWUoIix0cmVuZF9xdWVyeVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgIixjcmVhdGVkX2F0KSxjb3VudHJ5IixzZXA9IiIpCiAgICB9CiAgICBlbHNlewogICAgICBpZihnZW9pbmZvPT0nc3RhdGUnKXsKICAgICAgICBnZW9pbmZvX3F1ZXJ5PXBhc3RlKCJTRUxFQ1Qgc3RyZnRpbWUoIix0cmVuZF9xdWVyeVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICIsY3JlYXRlZF9hdCkgQVMgIix0cmVuZF9xdWVyeVsyXSwiLCAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvdW50KCopIEFTIG51bWJlciwgY291bnRyeSwgc3RhdGUsICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXZnKHNlbnRpbWVudF9zY29yZSkgQVMgc2VudGltZW50X3Njb3JlICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRlJPTSBDb3JvbmF2aXJ1c1R3ZWV0c0dlbyAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxFRlQgSk9JTiBUd2VldHNHZW9TZW50aW1lbnQgT04gIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDb3JvbmF2aXJ1c1R3ZWV0c0dlby5zdGF0dXNfaWQ9IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUd2VldHNHZW9TZW50aW1lbnQuc3RhdHVzX2lkICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTEVGVCBKT0lOIEdlb0RldGFpbCBPTiAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvcm9uYXZpcnVzVHdlZXRzR2VvLmxhdD1HZW9EZXRhaWwubGF0ICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQU5EIENvcm9uYXZpcnVzVHdlZXRzR2VvLmxuZz1HZW9EZXRhaWwubG5nIixzZXA9IiIpCiAgICAgICAgZ3JvdXBfcXVlcnk9cGFzdGUoIiBHUk9VUCBCWSBzdHJmdGltZSgiLHRyZW5kX3F1ZXJ5WzFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICIsY3JlYXRlZF9hdCksY291bnRyeSxzdGF0ZSIsc2VwPSIiKQogICAgICB9CiAgICAgIGVsc2V7CiAgICAgICAgaWYoZ2VvaW5mbz09J2NpdHknKXsKICAgICAgICAgIGdlb2luZm9fcXVlcnk9cGFzdGUoIlNFTEVDVCBzdHJmdGltZSgiLHRyZW5kX3F1ZXJ5WzFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiLGNyZWF0ZWRfYXQpIEFTICIsdHJlbmRfcXVlcnlbMl0sIiwgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvdW50KCopIEFTIG51bWJlciwgY291bnRyeSwgc3RhdGUsIGNpdHksICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhdmcoc2VudGltZW50X3Njb3JlKSBBUyBzZW50aW1lbnRfc2NvcmUgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZST00gQ29yb25hdmlydXNUd2VldHNHZW8gIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxFRlQgSk9JTiBUd2VldHNHZW9TZW50aW1lbnQgT04gIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvcm9uYXZpcnVzVHdlZXRzR2VvLnN0YXR1c19pZD0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHdlZXRzR2VvU2VudGltZW50LnN0YXR1c19pZCAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTEVGVCBKT0lOIEdlb0RldGFpbCBPTiAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29yb25hdmlydXNUd2VldHNHZW8ubGF0PUdlb0RldGFpbC5sYXQgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFORCBDb3JvbmF2aXJ1c1R3ZWV0c0dlby5sbmc9R2VvRGV0YWlsLmxuZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iIikKICAgICAgICAgIGdyb3VwX3F1ZXJ5PXBhc3RlKCIgR1JPVVAgQlkgc3RyZnRpbWUoIix0cmVuZF9xdWVyeVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICIsY3JlYXRlZF9hdCksY291bnRyeSxzdGF0ZSxjaXR5IixzZXA9IiIpCiAgICAgICAgfQogICAgICAgIGVsc2V7CiAgICAgICAgICBzdG9wKCJUaGUgZ2VvaW5mbyBjYW4gb25seSBiZSAnTlVMTCcsICdjaXR5JywgJ3N0YXRlJyBvciAnY291bnRyeScuIikKICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9CmBgYAoKCmBgYHtyfQogIyBBZGQga2V5d29yZHMgY29uZGl0aW9ucyBhY2NvcmRpbmcgdG8gJ2tleXdvcmRzJyAKICBpZihpcy5udWxsKGtleXdvcmRzKSl7CiAgICBrZXl3b3Jkc19xdWVyeT0nJwogIH0KICBlbHNlewogICAgZm9yKGkgaW4gMTpsZW5ndGgoa2V5d29yZHMpKXsKICAgICAgaWYoaT09MSl7CiAgICAgICAga2V5d29yZHNfcXVlcnk9cGFzdGUoIiAoKHRleHQgTElLRSAnJSIsa2V5d29yZHNbaV0sIiUnKSIsc2VwPSIiKQogICAgICB9CiAgICAgIGVsc2V7CiAgICAgICAga2V5d29yZHNfcXVlcnk9a2V5d29yZHNfcXVlcnklPiUKICAgICAgICAgIHBhc3RlKCJPUiAodGV4dCBMSUtFICclIixrZXl3b3Jkc1tpXSwiJScpIixzZXA9IiIpCiAgICAgIH0KICAgIH0KICAgIGtleXdvcmRzX3F1ZXJ5PXBhc3RlKGtleXdvcmRzX3F1ZXJ5LCIpICIsc2VwPSIiKQogIH0KICAjIEFkZCBwZXJpb2QgY29uZGl0aW9ucyBhY2NvcmRpbmcgdG8gJ3BlcmlvZCcKICBpZihpcy5udWxsKHBlcmlvZCkpewogICAgcGVyaW9kX3F1ZXJ5PScnCiAgfQogIGVsc2V7CiAgICBpZihsZW5ndGgocGVyaW9kKT09Mil7CiAgICAgIHBlcmlvZF9xdWVyeT1wYXN0ZSgiIChzdHJmdGltZSgnJVktJW0tJWQgJUg6JU06JVMnLGNyZWF0ZWRfYXQpPj0iLAogICAgICAgICAgICAgICAgICAgICAgICAgInN0cmZ0aW1lKCclWS0lbS0lZCAlSDolTTolUycsJyIscGVyaW9kWzFdLCInKSAiLAogICAgICAgICAgICAgICAgICAgICAgICAgIkFORCBzdHJmdGltZSgnJVktJW0tJWQgJUg6JU06JVMnLGNyZWF0ZWRfYXQpPD0iLAogICAgICAgICAgICAgICAgICAgICAgICAgInN0cmZ0aW1lKCclWS0lbS0lZCAlSDolTTolUycsJyIscGVyaW9kWzJdLCInKSkgIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iIikKICAgIH0KICAgIGVsc2V7CiAgICAgIHN0b3AoIlRoZSB0aW1lIHBlcmlvZCBzaG91bGQgYmUgYSB2ZWN0b3Igd2l0aCBsZW5ndGggMi4iKSAKICAgIH0KICB9CiAgIyBXcml0ZSBTUUwKICBpZihwZXJpb2RfcXVlcnk9PScnKXsKICAgIGlmKGtleXdvcmRzX3F1ZXJ5PT0nJyl7CiAgICAgIHF1ZXJ5PXBhc3RlKGdlb2luZm9fcXVlcnksZ3JvdXBfcXVlcnksc2VwPSIiKQogICAgfQogICAgZWxzZXsKICAgICAgcXVlcnk9cGFzdGUoZ2VvaW5mb19xdWVyeSwiIFdIRVJFIixrZXl3b3Jkc19xdWVyeSxncm91cF9xdWVyeSxzZXA9IiIpCiAgICB9CiAgfQogIGVsc2V7CiAgICBpZihrZXl3b3Jkc19xdWVyeT09JycpewogICAgICBxdWVyeT1wYXN0ZShnZW9pbmZvX3F1ZXJ5LCIgV0hFUkUiLHBlcmlvZF9xdWVyeSxncm91cF9xdWVyeSxzZXA9IiIpCiAgICB9CiAgICBlbHNlewogICAgICBxdWVyeT1wYXN0ZShnZW9pbmZvX3F1ZXJ5LCIgV0hFUkUiLHBlcmlvZF9xdWVyeSwiQU5EIixrZXl3b3Jkc19xdWVyeSwKICAgICAgICAgICAgICAgICAgZ3JvdXBfcXVlcnksc2VwPSIiKQogICAgfQogIH0KICAjIE9idGFpbiBEYXRhCiAgZGJHZXRRdWVyeShjb25uLHF1ZXJ5KQp9CgojIGNvbm5lY3QgdG8gZGF0YWJhc2UKCmRicGF0aD0iL1VzZXJzL21hYy9EZXNrdG9wL0NvdmlkLXR3ZWV0cy1lbi5kYiIKY29ubj1kYkNvbm5lY3QoU1FMaXRlKCksZGJwYXRoKQoKIyBsb2FkIHNwcmVhZCBkYXRhCnNwcmVhZF9kYXRhPXJlYWQuY3N2KCIvVXNlcnMvbWFjL0Rlc2t0b3Avc2hpbnkvdXNfY292aWQxOV9kYWlseS5jc3YiLGhlYWRlcj1UUlVFKSU+JQogIHRyYW5zbXV0ZShkYXRlPXltZChkYXRlKSwKICAgICAgICAgICAgcG9zaXRpdmVJbmNyZWFzZT1wb3NpdGl2ZUluY3JlYXNlLAogICAgICAgICAgICBkZWF0aEluY3JlYXNlPWRlYXRoSW5jcmVhc2UpJT4lCiAgYXJyYW5nZShkZXNjKGRhdGUpKSU+JQogIC5bNjg6MTAwLF0KCkdlb3Bsb3Q9Z2V0VHdpdHRlclRyZW5kKGNvbm4sZ2VvaW5mbz0nc3RhdGUnLHBlcmlvZD1OVUxMKSU+JQogIGZpbHRlcihjb3VudHJ5PT0nVW5pdGVkIFN0YXRlcycpJT4lCiAge21lcmdlKHJlYWQuY3N2KCIvVXNlcnMvbWFjL0Rlc2t0b3Avc2hpbnkvdXNfc3RhdGVzX2RhaWx5LmNzdiIsaGVhZGVyPVRSVUUpLC4sYnk9InN0YXRlIil9JT4lCiAge211dGF0ZSguLGhvdmVyPXdpdGgoLixwYXN0ZShzdGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+IDxicj4gUG9zaXRpdmU6Iixwb3NpdGl2ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+IGRlYXRoOiIsZGVhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPiBudW1iZXIgb2YgdHdlZXRzIixudW1iZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPiBzZW50aW1lbnQgc2NvcmUgb2YgdGhpcyBzdGF0ZSIsc2VudGltZW50X3Njb3JlKSkpfQoKYXk9bGlzdCh0aWNrZm9udD1saXN0KGNvbG9yPSJyZWQiKSxvdmVybGF5aW5nPSJ5IixzaWRlPSJyaWdodCIsdGl0bGU9ImZyZXF1ZW5jeSIpCgpnIDwtIGxpc3QoCiAgc2NvcGUgPSAndXNhJywKICBwcm9qZWN0aW9uID0gbGlzdCh0eXBlID0gJ2FsYmVycyB1c2EnKSwKICBzaG93bGFrZXMgPSBUUlVFLAogIGxha2Vjb2xvciA9IHRvUkdCKCd3aGl0ZScpKQpgYGAKCgpWaXN1YWxpemF0aW9uCgpgYGB7cn0KCiMgRmlyc3RseSBtYWtlIGEgd29yZCBmcmVxdWVuY3kgcGxvdAojIGNvbm5lY3QgdG8gZGF0YSBiYXNlCmNvbm49ZGJDb25uZWN0KFNRTGl0ZSgpLGRicGF0aCkKIyBnZXQgdHdpdHRlciBkYXRhIHdpdGggZ2VvIGluZm9ybWF0aW9uCiMgdHdlZXRzR2VvPWdldFR3aXR0ZXJEYXRhKGNvbm4scGVyaW9kID0gTlVMTCkKdHdlZXRzR2VvPWdldFR3aXR0ZXJEYXRhKGNvbm4scGVyaW9kID0gTlVMTCxrZXl3b3JkcyA9IGMoJ21hc2snLCdOOTUnKSkKIyBnZXQgdHdpdHRlciBkYXRhIHdpdGggZ2l2aW5nIGtleXdvcmRzIGFuZCB0aW1lCgptYXNrIDwtIGdldFR3aXR0ZXJUcmVuZChjb25uLGdlb2luZm8gPSBOVUxMLGtleXdvcmRzID0gYygnbWFzaycsJ045NScpKQoKIyBtYWtpbmcgYSB3b3JkIGZyZXF1ZW50IHBsb3Qgb2YgbWFzayByZWxhdGVkIGRhdGEuCmdncGxvdChkYXRhID0gbWFzaywgYWVzKHggPSB4LCB5ID0gbnVtYmVyKSkgKwogIGdlb21fYXJlYShjb2xvcj0iYmx1ZSIsZmlsbD0icHVycGxlIixhbHBoYT0uMikKCiMgbWFraW5nIGEgc2VudGltZW50IHNjb3JlIHBsb3Qgb2YgbWFzayByZWxhdGVkIGRhdGEuCmdncGxvdChkYXRhID0gbWFzaywgYWVzKHggPSB4LCB5ID0gc2VudGltZW50X3Njb3JlKSkgKwogIGdlb21fbGluZSgpKwogIGdlb21fcG9pbnQoc2l6ZT00LHNoYXBlPTIyLGNvbG9yPSJkYXJrcmVkIixmaWxsPSJwaW5rIikKIyBkaXNjb25uZWN0IGRhdGEgYmFzZQpkYkRpc2Nvbm5lY3QoY29ubikKCmBgYAoKYGBge3J9CgojIGxvYWQgc3ByZWFkIGRhdGEKZGFpbHkgPC0gcmVhZC5jc3YoZmlsZT0gIi9Vc2Vycy9tYWMvRGVza3RvcC9UcmluaXR5L3VzX2NvdmlkMTlfZGFpbHkuY3N2IiwgaGVhZGVyPVRSVUUpCnNwcmVhZF9kYXRhIDwtIHNlbGVjdChkYWlseSxkYXRlLGhvc3BpdGFsaXplZEN1bXVsYXRpdmUsZGVhdGgsZGVhdGhJbmNyZWFzZSxuZWdhdGl2ZUluY3JlYXNlLHBvc2l0aXZlSW5jcmVhc2UpJT4lCiAgbXV0YXRlKGRhdGVfbmV3PXltZChkYXRlKSklPiUKICBhcnJhbmdlKGRhaWx5LCBkZXNjKGRhdGVfbmV3KSkKc3ByZWFkX2RhdGEgPC0gc3ByZWFkX2RhdGFbNjg6MTAwLF0KCgojIHVzaW5nIHBsb3RseSBwYWNrYWdlIHRvIG1ha2UgYSBwbG90IHRoYXQgYm90aCBoYXZlIGRlYXRoLCBzZW50aW1lbnQgYW5kIGZyZXF1ZW5jeQpwb3NpdGl2ZWluY3JlYXNlIDwtIHNwcmVhZF9kYXRhWyw2XQpmcmVxdWVuY3kgPC0gbWFzayRudW1iZXIKc2VudGltZW50IDwtIG1hc2skc2VudGltZW50X3Njb3JlCmRhdGUgPC1zcHJlYWRfZGF0YSRkYXRlX25ldwpkYXRhIDwtIGRhdGEuZnJhbWUoZGF0ZSwgcG9zaXRpdmVpbmNyZWFzZSwgZnJlcXVlbmN5LCBzZW50aW1lbnQpCgpheSA8LSBsaXN0KAogIHRpY2tmb250ID0gbGlzdChjb2xvciA9ICJibHVlIiksCiAgb3ZlcmxheWluZyA9ICJ5IiwKICBzaWRlID0gInJpZ2h0IiwKICB0aXRsZSA9ICJ0aGUgZnJlcXVlbmN5IG9mICIKICApCgoKCmZpZyA8LSBwbG90X2x5KGRhdGEsIHggPSB+ZGF0ZSwgeSA9IH5wb3NpdGl2ZWluY3JlYXNlLCBuYW1lID0gJ3Bvc2l0aXZlaW5jcmVhc2UnLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLGNvbG9yID0gSSgiYmxhY2siKSkgCmZvcihpIGluIDE6MzIpCiAgaWYoKHNlbnRpbWVudFtpXStzZW50aW1lbnRbaSsxXSkvMiA+IDAgKXsKICAgIGZpZyA8LWZpZyAlPiUgYWRkX3RyYWNlKHkgPSBmcmVxdWVuY3lbaTooaSsxKV0sIHg9ZGF0ZVtpOihpKzEpXSwgc2hvd2xlZ2VuZCA9IEZBTFNFLCBtb2RlID0gJ2xpbmVzJywgbWFya2VyPWxpc3Qoc3ltYm9sID0gOCxzaXplPTEwKSwgeWF4aXMgPSAieTIiLGNvbG9yID0gSSgiZ3JlZW4iKSkgCiAgfWVsc2V7CiAgICBmaWcgPC1maWcgJT4lIGFkZF90cmFjZSh5ID0gZnJlcXVlbmN5W2k6KGkrMSldLCB4PWRhdGVbaTooaSsxKV0sIG5hbWUgPSBOQSwgc2hvd2xlZ2VuZCA9IEZBTFNFLCAgbW9kZSA9ICdsaW5lcyttYXJrZXJzJywgbWFya2VyPWxpc3Qoc3ltYm9sID0gMixzaXplPTEwKSx5YXhpcyA9ICJ5MiIsY29sb3IgPSBJKCJyZWQiKSkgICAgICAgCiAgICB9CmZpZyA8LSBmaWcgJT4lIGxheW91dCgKICB0aXRsZSA9ICJTdGF0aXN0aWNzIGFib3V0ICdtYXNrLCBOOTUnIiwgeWF4aXMyID0gYXksCiAgeGF4aXMgPSBsaXN0KHRpdGxlPSJ4IikpCmZpZyAgICAgICAgICAgICAgCgoKCgppbnB1dD1pbnB1dCR0ZXh0JT4lCiAgICAgIHN0cl9zcGxpdCgnIycpJT4lCiAgICAgIC5bWzFdXQogICAgZGF0YT1nZXRUd2l0dGVyVHJlbmQoY29ubixnZW9pbmZvID0gTlVMTCxrZXl3b3Jkcz1pbnB1dCklPiUKICAgICAge2RhdGEuZnJhbWUoZGF0ZT1zcHJlYWRfZGF0YSRkYXRlLAogICAgICAgICAgICAgICAgICBwb3NpdGl2ZUluY3JlYXNlPXNwcmVhZF9kYXRhJHBvc2l0aXZlSW5jcmVhc2UsCiAgICAgICAgICAgICAgICAgIGZyZXF1ZW5jeT0uJG51bWJlciwKICAgICAgICAgICAgICAgICAgc2VudGltZW50PS4kc2VudGltZW50X3Njb3JlKX0KCiAgICBmaWc9cGxvdF9seShkYXRhLHg9fmRhdGUseT1+cG9zaXRpdmVJbmNyZWFzZSxjb2xvcj1JKCdibGFjaycpLAogICAgICAgICAgICAgICAgbmFtZT0ncG9zaXRpdmVJbmNyZWFzZScsdHlwZSA9J3NjYXR0ZXInLG1vZGU9J2xpbmVzK21hcmtlcnMnKSAKICAgIG49bnJvdyhkYXRhKQogICAgY29sb3I9ZGF0YSRzZW50aW1lbnQlPiUKICAgICAgeyguWzE6KG4tMSldKy5bMjpuXSkvMn0lPiUKICAgICAge2lmZWxzZSguPjAsJ2dyZWVuJywncmVkJyl9CiAgICAKICAgIGZpZz1maWclPiUKICAgICAgYWRkX3RyYWNlKHg9ZGF0YSRkYXRlWzE6Ml0seT1kYXRhJGZyZXF1ZW5jeVsxOjJdLGNvbG9yPUkoJ2JsdWUnKSwKICAgICAgICAgICAgICAgIG5hbWU9aW5wdXRbMV0sbW9kZT0nbGluZXMrbWFya2VycycseWF4aXM9InkyIiwKICAgICAgICAgICAgICAgIHZpc2libGU9J2xlZ2VuZG9ubHknKQogICAgZm9yKGkgaW4gMToobi0xKSkKICAgICAgZmlnPWZpZyU+JQogICAgICAgIGFkZF90cmFjZSh4PWRhdGEkZGF0ZVtpOihpKzEpXSx5PWRhdGEkZnJlcXVlbmN5W2k6KGkrMSldLGNvbG9yPUkoY29sb3JbaV0pLAogICAgICAgICAgICAgICAgICBtYXJrZXI9bGlzdChzeW1ib2wgPSAyLHNpemU9MTApLHNob3dsZWdlbmQ9Rixtb2RlPSdsaW5lcyttYXJrZXJzJyx5YXhpcz0ieTIiKQogICAgZmlnJT4lbGF5b3V0KHRpdGxlPSJUd2l0dGVyIFNlbnRpbWVudCB0cmVuZHMiLHlheGlzMj1heSx4YXhpcz1saXN0KHRpdGxlPSJ4IikpCiAgICAKCgogIAojIOeWq+aDheaVsOaNrgpzcHJlYWRfZGF0YT1yZWFkLmNzdignL1VzZXJzL21hYy9EZXNrdG9wL1RyaW5pdHkvdXNfY292aWQxOV9kYWlseS5jc3YnKSU+JQogIHRyYW5zbXV0ZShkYXRlPXltZChkYXRlKSxwb3NpdGl2ZUluY3JlYXNlPXBvc2l0aXZlSW5jcmVhc2UsCiAgICAgICAgICAgIGRlYXRoSW5jcmVhc2U9ZGVhdGhJbmNyZWFzZSkKIyDlhbPplK7or40x5pWw5o2uCmlucHV0PSdNYXNrI045NSPlj6PnvaknJT4lCiAgc3RyX3NwbGl0KCcjJyklPiUKICAuW1sxXV0KZGF0YTE9Z2V0VHdpdHRlclRyZW5kKGNvbm4sZ2VvaW5mbyA9IE5VTEwsa2V5d29yZHM9aW5wdXQpJT4lCiAgbXV0YXRlKGRhdGU9eW1kKGRhdGUpKQojIOWFs+mUruivjTLmlbDmja4KaW5wdXQ9J2xvY2tkb3duI3N0YXkgaG9tZSclPiUKICBzdHJfc3BsaXQoJyMnKSU+JQogIC5bWzFdXQoKZGF0YTI9Z2V0VHdpdHRlclRyZW5kKGNvbm4sZ2VvaW5mbyA9IE5VTEwsa2V5d29yZHM9aW5wdXQpJT4lCiAgbXV0YXRlKGRhdGU9eW1kKGRhdGUpKQpzcHJlYWRfZW5kPW1heChtYXgoZGF0YTEkZGF0ZSksbWF4KGRhdGEyJGRhdGUpKQpzcHJlYWRfc3RhcnQ9bWluKG1pbihkYXRhMSRkYXRlKSxtaW4oZGF0YTIkZGF0ZSkpCgpkYXRhXzEgPC0gZGF0YTElPiUKICBsZWZ0X2pvaW4oc3ByZWFkX2RhdGEsJ2RhdGUnKQoKZGF0YV8yIDwtIG1lcmdlKGRhdGExLGRhdGEyLGJ5PSJkYXRlIiklPiUKICAgbGVmdF9qb2luKHNwcmVhZF9kYXRhLCdkYXRlJykKI2RhdGExCiBmaWc9cGxvdF9seShkYXRhXzEseD1+ZGF0ZSx5PX5wb3NpdGl2ZUluY3JlYXNlLGNvbG9yPUkoJ2JsYWNrJyksCiAgICAgICAgICAgICAgICBuYW1lPSdwb3NpdGl2ZUluY3JlYXNlJyx0eXBlID0nc2NhdHRlcicsbW9kZT0nbGluZXMrbWFya2VycycpIAogICAgbj1ucm93KGRhdGFfMSkKICAgIGNvbG9yPWRhdGEkc2VudGltZW50JT4lCiAgICAgIHsoLlsxOihuLTEpXSsuWzI6bl0pLzJ9JT4lCiAgICAgIHtpZmVsc2UoLj4wLCdncmVlbicsJ3JlZCcpfQogICAgCiAgICBmaWc9ZmlnJT4lCiAgICAgIGFkZF90cmFjZSh4PWRhdGFfMSRkYXRlWzE6Ml0seT1kYXRhXzEkZnJlcXVlbmN5WzE6Ml0sY29sb3I9SSgnYmx1ZScpLAogICAgICAgICAgICAgICAgbmFtZT1pbnB1dFsxXSxtb2RlPSdsaW5lcyttYXJrZXJzJyx5YXhpcz0ieTIiLAogICAgICAgICAgICAgICAgdmlzaWJsZT0nbGVnZW5kb25seScpCiAgICBmb3IoaSBpbiAxOihuLTEpKQogICAgICBmaWc9ZmlnJT4lCiAgICAgICAgYWRkX3RyYWNlKHg9ZGF0YV8xJGRhdGVbaTooaSsxKV0seT1kYXRhXzEkZnJlcXVlbmN5W2k6KGkrMSldLGNvbG9yPUkoY29sb3JbaV0pLAogICAgICAgICAgICAgICAgICBtYXJrZXI9bGlzdChzeW1ib2wgPSAyLHNpemU9MTApLHNob3dsZWdlbmQ9Rixtb2RlPSdsaW5lcyttYXJrZXJzJyx5YXhpcz0ieTIiKQogICAgZmlnJT4lbGF5b3V0KHRpdGxlPSJUd2l0dGVyIFNlbnRpbWVudCB0cmVuZHMiLHlheGlzMj1heSx4YXhpcz1saXN0KHRpdGxlPSJ4IikpCiAgICAKI2RhdGEyCmF5IDwtIGxpc3QoCiAgdGlja2ZvbnQgPSBsaXN0KGNvbG9yID0gImJsdWUiKSwKICBvdmVybGF5aW5nID0gInkiLAogIHNpZGUgPSAicmlnaHQiLAogIHRpdGxlID0gInRoZSBmcmVxdWVuY3kgb2YgaW5wdXQiCiAgKSAgICAKICAgCiAgICBmaWc9cGxvdF9seShkYXRhXzIseD1+ZGF0ZSx5PX5wb3NpdGl2ZUluY3JlYXNlLGNvbG9yPUkoJ2JsYWNrJyksCiAgICAgICAgICAgICAgICBuYW1lPSdwb3NpdGl2ZUluY3JlYXNlJyx0eXBlID0nc2NhdHRlcicsbW9kZT0nbGluZXMrbWFya2VycycpIAogICAgbj1ucm93KGRhdGFfMikKICAgIGNvbG9yLng9ZGF0YSRzZW50aW1lbnRfc2NvcmUueCU+JQogICAgICB7KC5bMToobi0xKV0rLlsyOm5dKS8yfSU+JQogICAgICB7aWZlbHNlKC4+MCwnZ3JlZW4nLCdyZWQnKX0KICAgIGNvbG9yLnk9ZGF0YSRzZW50aW1lbnRfc2NvcmUueSU+JQogICAgICB7KC5bMToobi0xKV0rLlsyOm5dKS8yfSU+JQogICAgICB7aWZlbHNlKC4+MCwnZ3JlZW4nLCdyZWQnKX0KICAgIAogICAgZmlnPWZpZyU+JQogICAgICBhZGRfdHJhY2UoeD1kYXRhXzIkZGF0ZVsxOjJdLHk9ZGF0YV8yJGZyZXF1ZW5jeS54WzE6Ml0sY29sb3I9SSgnYmx1ZScpLAogICAgICAgICAgICAgICAgbmFtZT1pbnB1dFsxXSxtb2RlPSdsaW5lcyttYXJrZXJzJyx5YXhpcz0ieTIiLAogICAgICAgICAgICAgICAgdmlzaWJsZT0nbGVnZW5kb25seScpCiAgICBmb3IoaSBpbiAxOihuLTEpKQogICAgICBmaWc9ZmlnJT4lCiAgICAgICAgYWRkX3RyYWNlKHg9ZGF0YV8yJGRhdGVbaTooaSsxKV0seT1kYXRhXzIkZnJlcXVlbmN5LnhbaTooaSsxKV0sY29sb3I9SShjb2xvci54W2ldKSwKICAgICAgICAgICAgICAgICAgbWFya2VyPWxpc3Qoc3ltYm9sID0gMixzaXplPTEwKSxzaG93bGVnZW5kPUYsbW9kZT0nbGluZXMrbWFya2VycycseWF4aXM9InkyIikKICAgIGZpZyU+JWxheW91dCh0aXRsZT0iVHdpdHRlciBTZW50aW1lbnQgdHJlbmRzIG9mIG1hc2siLHlheGlzMj1heSx4YXhpcz1saXN0KHRpdGxlPSJ4IikpCgogICAgZmlnPWZpZyU+JQogICAgICBhZGRfdHJhY2UoeD1kYXRhXzIkZGF0ZVsxOjJdLHk9ZGF0YV8yJGZyZXF1ZW5jeS55WzE6Ml0sY29sb3I9SSgneWVsbG93JyksCiAgICAgICAgICAgICAgICBuYW1lPWlucHV0WzFdLG1vZGU9J2xpbmVzK21hcmtlcnMnLHlheGlzPSJ5MiIsCiAgICAgICAgICAgICAgICB2aXNpYmxlPSdsZWdlbmRvbmx5JykKICAgIGZvcihpIGluIDE6KG4tMSkpCiAgICAgIGZpZz1maWclPiUKICAgICAgICBhZGRfdHJhY2UoeD1kYXRhXzIkZGF0ZVtpOihpKzEpXSx5PWRhdGFfMiRmcmVxdWVuY3kueVtpOihpKzEpXSxjb2xvcj1JKGNvbG9yLnlbaV0pLAogICAgICAgICAgICAgICAgICBtYXJrZXI9bGlzdChzeW1ib2wgPSA4LHNpemU9MTApLHNob3dsZWdlbmQ9Rixtb2RlPSdsaW5lcyttYXJrZXJzJyx5YXhpcz0ieTIiKQogICAgZmlnJT4lbGF5b3V0KHRpdGxlPSJUd2l0dGVyIFNlbnRpbWVudCB0cmVuZHMgb2YgbG9ja2Rvd24iLHlheGlzMj1heSx4YXhpcz1saXN0KHRpdGxlPSJ4IikpCiAgICAKICBmaWcgIAogICAgCiAgICAKICAgIAogICAgCmBgYAoKIyBHZW9tIHBsb3RzCgpgYGB7cn0KCiMgRm9yIHRoZSBnZW8gcGxvdAojc3RhdGVzIDwtIGMoInRleGFzIiwib2tsYWhvbWEiLCJrYW5zYXMiLCJsb3Vpc2lhbmEiLCJhcmthbnNhcyIsIm1pc3NvdXJpIiwiaW93YSIsCiMid2lzY29uc2luIiwibWljaGlnYW4iLCJpbGxpbm9pcyIsImluZGlhbmEiLCJvaGlvIiwia2VudHVja3kiLCJ0ZW5uZXNzZWUiLAojImFsYWJhbWEiLCJtaXNzaXNzaXBwaSIsImZsb3JpZGEiLCJnZW9yZ2lhIiwic291dGggY2Fyb2xpbmEiLCJub3J0aCBjYXJvbGluYSIsCiMidmlyZ2luaWEiLCJ3ZXN0IHZpcmdpbmlhIiwibWFyeWxhbmQiLCJkZWxhd2FyZSIsInBlbm5zeWx2YW5pYSIsIm5ldyBqZXJzZXkiLAojIm5ldyB5b3JrIiwiY29ubmVjdGljdXQiLCJyaG9kZSBpc2xhbmQiLCJtYXNzYWNodXNldHRzIiwidmVybW9udCIsCiMibmV3IGhhbXBzaGlyZSIsIm1haW5lIikKCiN0dXJuIGRhdGEgZnJvbSB0aGUgbWFwcyBwYWNrYWdlIGluIHRvIGEgZGF0YSBmcmFtZSBzdWl0YWJsZSBmb3IgcGxvdHRpbmcgd2l0aCBnZ3Bsb3QyCiNtYXBfc3RhdGVzIDwtIG1hcF9kYXRhKCJjb3VudHkiLCBzdGF0ZXMpCiMgVG8gZHJhdyB0aGUgYm9yZGVyLWJ5IGdyb3VwIDEwCiNtYXBfc3RhdGVzX2JvcmRlciA8LSBtYXBfZGF0YSgic3RhdGUiLHN0YXRlcykKdHdlZXRzR2VvIDwtIHR3ZWV0c0dlbyAlPiUKICBncm91cF9ieShzdGF0ZSklPiUKICBtdXRhdGUoc3VtID0gbigpLAogICAgICAgICBsb25nPWxuZykKCgp0d2VldHNHZW9fRW4gPC0gZmlsdGVyKHR3ZWV0c0dlbywgY291bnRyeSA9PSAnVW5pdGVkIFN0YXRlcycpClZpZXcodHdlZXRzR2VvX0VuKQojIHN1bW1hcnkodHdlZXRzR2VvX0VuJHN1bSkKIyBkaXZpZGUgc3VtIG9mIHR3ZWV0cyBpbiBlYWNoIHN0YXRlIGludG8gNCBwYXJ0cyB3aGljaCBpcyAKIyB0d2VldHNHZW9fRW4kY3V0IDwtIGN1dCh0d2VldHNHZW9fRW4kc3VtLAojICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcz1jKDAsMTY1OCw3ODkwLDE1MDY0LDE4MjA2KSwKICMgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmxvd2VzdCA9IFQpCgp0d2VldHNHZW9fRW5fMSA8LSB0d2VldHNHZW9fRW4lPiUKICBtdXRhdGUobG9uZz1hcy5kb3VibGUobG9uZyksCiAgICAgICAgIGxhdD1hcy5kb3VibGUobGF0KSklPiUKICBncm91cF9ieShzdGF0ZSklPiUKICBzdW1tYXJpemUoc3VtX3N0YXRlcz1uKCksIG1lYW5fc2VudGltZW50PW1lYW4oc2VudGltZW50X3Njb3JlKSxkYXRlKQoKCiMgTWFrZSB0aGUgZ2VvIHBsb3QgaW4gZ2dwbG90CiN0d2VldHNHZW9fcGxvdCA8LSBnZ3Bsb3QoKSsKIyAgZ2VvbV9wb2x5Z29uKHR3ZWV0c0dlb19Fbl8xLCBtYXBwaW5nPWFlcyh4PWxvbmcsIHk9bGF0LCBncm91cD1jaXR5KSwgZmlsbD1jdXQpKwogIyBjb25uZWN0cyB0aGUgb2JzZXJ2YXRpb25zIGluIHRoZSBvcmRlciBpbiB3aGljaCB0aGV5IGFwcGVhciBpbiB0aGUgJ21hcF9zdGF0ZXMnCiMgIGdlb21fcGF0aChtYXBfc3RhdGVzLCBtYXBwaW5nPWFlcyh4PWxvbmcsIHk9bGF0LGdyb3VwPWNpdHkpLGNvbG9yPSJncmV5IikrCiAjICBBZGQgdGhlIGJvcmRlciB0byBtYWtlIGl0IGNsZWFyLWJ5IEdyb3VwIDEwCiMgIGdlb21fcGF0aChtYXBfc3RhdGVzX2JvcmRlciwgbWFwcGluZz1hZXMoeD1sb25nLCB5PWxhdCxncm91cD1jaXR5KSxjb2xvcj0iYmxhY2siKSsKICAKICMgIGRpc3BsYXkgZGlzY3JldGUgdmFsdWVzIG9uIGEgbWFwCiMgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IkJsdWVzIikrCiAgIyBjaGFuZ2UgdGhlIG5hbWUgb2YgeCwgeSwgYW5kIHRpdGxlCiMgIHhsYWIoIkxvbmd0aXR1ZGUiKSt5bGFiKCJMYXRpdHVkZSIpK2dndGl0bGUoInR3ZWV0c0dlbyIpKwogIyAgYWRkIG1hcmtzCiMgIGxhYnMoZmlsbD0ibnVtYmVyIG9mIHR3ZWV0cyIpKwojICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTgpKQoKIyB0d2VldHNHZW9fcGxvdAoKCkFwcjQwIDwtIHJlYWQuY3N2KGZpbGU9ICIvVXNlcnMvbWFjL0Rlc2t0b3AvVHJpbml0eS91c19zdGF0ZXNfZGFpbHkuY3N2IiwgaGVhZGVyPVRSVUUpCkdlb3Bsb3QgPC0gbWVyZ2UoQXByNDAsdHdlZXRzR2VvX0VuXzEgLCBieT1jKCJzdGF0ZSIpKQpHZW9wbG90JGhvdmVyIDwtIHdpdGgoR2VvcGxvdCwgcGFzdGUoc3RhdGUsICc8YnI+JywgICc8YnI+JywgIlBvc2l0aXZlOiIsIHBvc2l0aXZlLCAiPGJyPiIsImRlYXRoOiIsIGRlYXRoLCI8YnI+IiwgIm51bWJlciBvZiB0d2VldHMiLCBzdW1fc3RhdGVzLCc8YnI+JywgInNlbnRpbWVudCBzY29yZSBvZiB0aGlzIHN0YXRlIiwgbWVhbl9zZW50aW1lbnQpKSAjcHV0IGRhdGEKCmcgPC0gbGlzdCgKICBzY29wZSA9ICd1c2EnLAogIHByb2plY3Rpb24gPSBsaXN0KHR5cGUgPSAnYWxiZXJzIHVzYScpLAogIHNob3dsYWtlcyA9IFRSVUUsCiAgbGFrZWNvbG9yID0gdG9SR0IoJ3doaXRlJykpCgoKCiMgY3JlYXRlIGRhdGEKY29ubj1kYkNvbm5lY3QoU1FMaXRlKCksZGJwYXRoKQpHZW9wbG90PWdldFR3aXR0ZXJUcmVuZChjb25uLGdlb2luZm89J3N0YXRlJyxwZXJpb2Q9TlVMTCx0cmVuZCA9ICJtb250aCIpJT4lCiAgZmlsdGVyKGNvdW50cnk9PSdVbml0ZWQgU3RhdGVzJyklPiUKICBtdXRhdGUobW9udGg9YXMuaW50ZWdlcihtb250aCkpJT4lCiAge21lcmdlKHJlYWQuY3N2KCIvVXNlcnMvbWFjL0Rlc2t0b3AvVHJpbml0eS91c19zdGF0ZXNfZGFpbHkuY3N2IixoZWFkZXI9VFJVRSksLixieT0ic3RhdGUiKX0lPiUKICB7bXV0YXRlKC4saG92ZXI9d2l0aCguLHBhc3RlKHN0YXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj4gPGJyPiBQb3NpdGl2ZToiLHBvc2l0aXZlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj4gRGVhdGg6IixkZWF0aCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+IE51bWJlciBvZiBUd2VldHMiLG51bWJlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+IFNlbnRpbWVudCBTY29yZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChzZW50aW1lbnRfc2NvcmUsMykpKSl9CiMgZGlzY29ubmVjdCBkYXRhIGJhc2UKZGJEaXNjb25uZWN0KGNvbm4pCiMgY3JlYXRlIHN0ZXBzIGFuZCBwbG90IGFsbCB0cmFjZXMKZmlnPXBsb3RfZ2VvKGxvY2F0aW9ubW9kZT0nVVNBLXN0YXRlcycpCm49R2VvcGxvdCRtb250aCU+JXVuaXF1ZSgpJT4lbGVuZ3RoKCkKdmlzaWJsZT1jKFQscmVwKEYsbi0xKSkKc3RlcHM9bGlzdCgpCmZvciAoaSBpbiAxOm4pIHsKICBmaWc9R2VvcGxvdFtHZW9wbG90JG1vbnRoPT1pLF0lPiUKICAgIHthZGRfdHJhY2UoZmlnLGxvY2F0aW9ucz0uJHN0YXRlLHo9LiRzZW50aW1lbnRfc2NvcmUsdGV4dD0uJGhvdmVyLAogICAgICAgICAgICAgICBob3ZlcmluZm89J3RleHQnLHZpc2libGU9dmlzaWJsZVtpXSwKICAgICAgICAgICAgICAgdHlwZT0nY2hvcm9wbGV0aCcsY29sb3JzPSJSZEJ1Iil9CiAgc3RlcHNbW2ldXT1saXN0KGFyZ3M9bGlzdCgndmlzaWJsZScsYyhyZXAoRixpLTEpLFQscmVwKEYsbi1pKSkpLAogICAgICAgICAgICAgICAgICBsYWJlbD1tb250aChpLFQpLG1ldGhvZD0ncmVzdHlsZScpCn0gIAojIGFkZCBzbGlkZXIgY29udHJvbCB0byBwbG90CmZpZyU+JWxheW91dCh0aXRsZT0iU2VudGltZW50IFNjb3JlIG9mIFN0YXRlcyIsCiAgICAgICAgICAgICBnZW89bGlzdChzY29wZT0ndXNhJyxwcm9qZWN0aW9uPWxpc3QodHlwZT0nYWxiZXJzIHVzYScpLAogICAgICAgICAgICAgICAgICAgICAgc2hvd2xha2VzPVQsbGFrZWNvbG9yPXRvUkdCKCd3aGl0ZScpKSwKICAgICAgICAgICAgIHNsaWRlcnM9bGlzdChsaXN0KGFjdGl2ZT0xLGN1cnJlbnR2YWx1ZT1saXN0KHByZWZpeD0iTW9udGg6ICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcHM9c3RlcHMpKSklPiVoaWRlX2NvbG9yYmFyKCkKCmBgYAoKCgoKCgoK